package com.eyas.software.pattern.eventbus.register.impl;

import com.eyas.software.pattern.eventbus.event.Event;
import com.eyas.software.pattern.eventbus.register.Register;
import com.eyas.software.pattern.eventbus.subscriber.Subscriber;
import com.eyas.software.pattern.eventbus.subscriber.impl.AnnotationSubscriber;
import com.eyas.software.pattern.eventbus.test.Observer1;
import sun.net.www.protocol.file.FileURLConnection;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 扫描带有@Subscriber注解的订阅者，并添加到注册器中进行维护
 */
public class AnnotationRegister implements Register {
    private final static String SCANNER_BASK_PACKAGE = "com.eyas.software.pattern.eventbus.test";
    //按照Event类型订阅
    private final Map<Event, CopyOnWriteArrayList<Subscriber>> cache = new ConcurrentHashMap<>();
    //映射关系，主要在unregister时使用，提高注销效率
    private final Map<Subscriber, List<Event>> mapper = new ConcurrentHashMap<>();

    public AnnotationRegister() {
        this.initRegister();
    }

    /**
     * 初始化时，扫描com.eyas.software.pattern.eventbus.test下及子包下所有的class，
     * 将带有@Subscriber注解的方法注册
     * NOTE:当前只扫描.class文件，不会扫描jar包，运行代码的时候不能在jar包下执行
     */
    private void initRegister() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if(classLoader==null)
            classLoader = ClassLoader.getSystemClassLoader();
        String basePath = AnnotationRegister.SCANNER_BASK_PACKAGE.replace(".","/");
        try {
            Enumeration<URL> urls = classLoader.getResources(basePath);
            List<String> classFiles = new ArrayList<>();
            while(urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if(url.getProtocol().equals("file")) {
                    File file = new File(url.toURI());
                    classFiles.addAll(this.getClassFile(file));
                }
            }

            if(classFiles.size()>0) {
                for(String className:classFiles) {
                    Class<?> subscriberClass = Class.forName(className);
                    this.registerClassDeclaredMethods(subscriberClass);
                }
            }
        } catch (IOException | URISyntaxException | ClassNotFoundException e) {
            e.printStackTrace();
            //扫描失败不影响后续操作
        }
    }

    /**
     * 遍历dir，包括子文件夹，将.class文件挑选出来
     * @param dir
     * @return
     */
    private List<String> getClassFile(File dir) {
        List<String> classFiles = new ArrayList<>();
        File[] files = dir.listFiles();
        if(files!=null&&files.length>0) {
            for(File file:files) {
                if(file.getName().contains(".class")) {
                    String basePath = AnnotationRegister.SCANNER_BASK_PACKAGE.replace(".",File.separator);
                    String fullPath = file.getPath();
                    String classFullName = fullPath.substring(fullPath.indexOf(basePath),fullPath.length()-6).replace(File.separator,".");
                    classFiles.add(classFullName);
                }else {
                    classFiles.addAll(getClassFile(file));
                }
            }
        }
        return classFiles;
    }

    /**
     * 扫描clazz及其父类，直到Object，这些类中所有带有@Subscriber注解的方法，并跟注解的配置进行注册
     * @param clazz
     */
    private void registerClassDeclaredMethods(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        if(methods.length!=0) {
            for(Method method:methods) {
                if(method.isAnnotationPresent(com.eyas.software.pattern.eventbus.annotation.Subscriber.class)) {
                    //为了保证Dispathcer能成功执行方法，需要将所有方法的accessible设置为true
                    method.setAccessible(true);
                    Class<?> eventType = method.getAnnotation(com.eyas.software.pattern.eventbus.annotation.Subscriber.class).eventType();
                    Event event = new Event();
                    event.setEventType(eventType);
                    Subscriber subscriber = new AnnotationSubscriber(clazz,method);
                    this.register(event,subscriber);
                }
            }
        }
        Class<?> superClass = clazz.getSuperclass();
        if(superClass!=Object.class)
            registerClassDeclaredMethods(superClass);
    }

    /**
     * 需要保证线程安全
     * @param event
     * @param subscriber
     */
    @Override
    public synchronized void register(Event event, Subscriber subscriber) {
        boolean hasRegistered = false; //subscriber是否已经监听了event
        if(this.cache.containsKey(event)) {
            CopyOnWriteArrayList<Subscriber> subscribers = this.cache.get(event);
            //如果已经注册过，不能再注册
            hasRegistered = !subscribers.addIfAbsent(subscriber);
        }else {
            CopyOnWriteArrayList<Subscriber> subscribers = new CopyOnWriteArrayList<>();
            subscribers.add(subscriber);
            this.cache.put(event,subscribers);
        }
        if(!hasRegistered) {
            if(this.mapper.containsKey(subscriber)) {
                this.mapper.get(subscriber).add(event);
            }else {
                List<Event> list = new ArrayList<>();
                list.add(event);
                this.mapper.put(subscriber,list);
            }
        }
    }

    /**
     * 需要保证线程安全
     * @param event
     * @param subscriber
     */
    @Override
    public synchronized void unregister(Event event, Subscriber subscriber) {
        CopyOnWriteArrayList<Subscriber> subscribers = this.cache.get(event);
        if(subscribers!=null&&subscribers.size()>0) {
            int index = subscribers.indexOf(subscriber);
            if(index>-1) {
                Subscriber key = subscribers.get(index);
                this.mapper.get(key).remove(event);
                if(this.mapper.get(key).size()==0)
                    this.mapper.remove(key);
                subscribers.remove(subscriber);
                if(subscribers.size()==0)
                    this.cache.remove(event);
            }
        }
    }

    @Override
    public List<Subscriber> getSubscribers(Event event) {
        return this.cache.get(event);
    }

    /**
     * 判断subscriber执行的方法是否带有@Subscriber注解,且方法的参数列表个数<=1，且参数类型=eventType
     *
     * @param event
     * @param subscriber
     * @return
     */
    private boolean supported(Event event, Subscriber subscriber) {
        return true;
    }

    /**
     * 判断方法是否带有@Subscriber注解,且方法的参数列表个数<=1，且参数类型=eventType
     *
     * @param event
     * @param method
     * @return
     */
    private boolean supported(Event event, Method method) {
        return true;
    }
}
